package top.heue.utils.compose

import java.io.File
import java.net.NetworkInterface
import java.security.MessageDigest

object DeviceFinger {

    /**
     * 返回 设备指纹，由设备类别前缀 + digest 组成
     * 组成原始数据：mac + cpu数 + 内存大小 + 主板序列号
     */
    fun getFingerprint(): String {
        val mac = getMacAddress()
        val cpuCount = Runtime.getRuntime().availableProcessors()
        val totalMem = getTotalPhysicalMemory()
        val boardSn = getMotherboardSN()

        val raw = "$mac|$cpuCount|$totalMem|$boardSn"
        val digest = MessageDigest.getInstance("SHA-256").digest(raw.toByteArray())
        val shortHex = digest.copyOfRange(0, 8).toHex()
        return "${getDeviceCategory()}_${shortHex}"
    }

    /* ---------------- 私有采集函数 ---------------- */
    private fun getMotherboardSN(): String = runCatching {
        File("/sys/class/dmi/id/board_serial").readText().trim()
    }.getOrElse {
        wmic("baseboard", "get", "SerialNumber").lineSequence()
            .firstOrNull { it.isNotBlank() && !it.contains("SerialNumber") }?.trim() ?: ""
    }

    private fun getBiosSN(): String = runCatching {
        File("/sys/class/dmi/id/bios_version").readText().trim()
    }.getOrElse {
        wmic("bios", "get", "SerialNumber").lineSequence()
            .firstOrNull { it.isNotBlank() && !it.contains("SerialNumber") }?.trim() ?: ""
    }

    private fun getCpuSerial(): String = runCatching {
        File("/proc/cpuinfo").readLines()
            .firstOrNull { it.startsWith("Serial") || it.startsWith("CPU serial") }?.substringAfter(":")?.trim() ?: ""
    }.getOrElse {
        wmic("cpu", "get", "ProcessorId").lineSequence()
            .firstOrNull { it.isNotBlank() && !it.contains("ProcessorId") }?.trim() ?: ""
    }

    private fun getMacAddress(): String = runCatching {
        NetworkInterface.getNetworkInterfaces().toList()
            .sortedBy { it.name }
            .firstOrNull { !it.isLoopback && it.hardwareAddress != null }
            ?.hardwareAddress?.joinToString("") { "%02X".format(it) } ?: ""
    }.getOrDefault("")

    /**
     * 总物理内存（字节或 kB，取决于平台获取途径），用于参与指纹生成
     */
    private fun getTotalPhysicalMemory(): String = runCatching {
        val mf = Class.forName("java.lang.management.ManagementFactory")
        val osMxBean = mf.getMethod("getOperatingSystemMXBean").invoke(null)
        val m = osMxBean.javaClass.methods.firstOrNull { it.name == "getTotalPhysicalMemorySize" }
        ((m?.invoke(osMxBean) as? Long) ?: 0L).toString()
    }.getOrElse {
        runCatching {
            File("/proc/meminfo").useLines { lines ->
                lines.firstOrNull { it.startsWith("MemTotal") }
            }?.substringAfter(":")?.trim()?.split(" ")?.firstOrNull() ?: ""
        }.getOrElse {
            wmic("ComputerSystem", "get", "TotalPhysicalMemory").lineSequence()
                .map { it.trim() }
                .firstOrNull { it.isNotEmpty() && it.all(Char::isDigit) } ?: ""
        }
    }

    /**
     * 返回当前设备类别：Windows/ Linux/ Android → w/ l/ a
     */
    fun getDeviceCategory(): String {
        val osName = System.getProperty("os.name")?.lowercase() ?: ""
        val isAndroid = runCatching { Class.forName("android.os.Build"); true }.getOrElse {
            val vendor = System.getProperty("java.vendor") ?: ""
            val runtime = System.getProperty("java.runtime.name") ?: ""
            vendor.contains("Android", ignoreCase = true) || runtime.contains("Android", ignoreCase = true)
        }
        return when {
            isAndroid -> "a"
            osName.contains("win") -> "w"
            else -> "l"
        }
    }

    /* 跨平台执行 wmic（仅 Windows） */
    private fun wmic(vararg args: String): String {
        val os = System.getProperty("os.name") ?: ""
        if (!os.contains("win", ignoreCase = true)) return ""
        return try {
            val proc = ProcessBuilder("wmic", *args).start()
            proc.inputStream.bufferedReader().use { it.readText() }
        } catch (e: Exception) {
            ""
        }
    }

    /* ByteArray → 十六进制字符串 */
    private fun ByteArray.toHex(): String = joinToString("") { "%02x".format(it) }
}
